package com.skt.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.skt.domain.DTO.TenantOrderQueryRequest;
import com.skt.domain.DTO.request.OrderCreateRequest;
import com.skt.domain.ENUM.AccountStatus;
import com.skt.domain.ENUM.UserRole;
import com.skt.domain.PO.Costume;
import com.skt.domain.PO.RentalOrder;
import com.skt.domain.PO.RentalOrderItem;
import com.skt.domain.PO.User;
import com.skt.domain.VO.OrderCreateResultVO;
import com.skt.domain.VO.OrderDetailVO;
import com.skt.domain.VO.OrderItemVO;
import com.skt.exception.AuthException;
import com.skt.exception.AuthErrorCode;
import com.skt.exception.OrderException;
import com.skt.exception.OrderErrorCode;
import com.skt.mapper.CostumeMapper;
import com.skt.mapper.RentalOrderMapper;
import com.skt.mapper.RentalOrderItemMapper;
import com.skt.mapper.UserMapper;
import com.skt.result.PageResult;
import com.skt.security.LoginUser;
import com.skt.service.TenantOrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class TenantOrderServiceImpl extends ServiceImpl<RentalOrderMapper, RentalOrder> implements TenantOrderService {

    private final UserMapper userMapper;
    private final CostumeMapper costumeMapper;
    private final RentalOrderMapper rentalOrderMapper;
    private final RentalOrderItemMapper rentalOrderItemMapper;

    /**
     * 创建订单
     *
     * @param createRequest 订单创建请求
     * @return 订单创建结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderCreateResultVO createOrder(OrderCreateRequest createRequest) {
        // 1. 获取并校验当前用户（租户）
        Long currentUserId = getCurrentUserId();

        // 2. 校验订单商品列表非空
        if (createRequest.getItems() == null || createRequest.getItems().isEmpty()) {
            throw OrderException.of(OrderErrorCode.ORDER_ITEMS_EMPTY);
        }

        // 3. 校验租赁日期范围合法性
        validateDateRange(createRequest);

        // 4. 批量校验服装商品及其所属商家状态
        Map<Long, Costume> validCostumes = validateAndFetchCostumes(createRequest);

        // 5. 根据租期和数量计算订单总金额
        BigDecimal totalAmount = calculateTotalAmount(createRequest, validCostumes);

        // 6. 构建并保存订单
        RentalOrder order = buildAndSaveOrder(createRequest, currentUserId, totalAmount);

        // 7. 返回创建成功的订单摘要信息
        long totalDays = ChronoUnit.DAYS.between(createRequest.getStartDate(), createRequest.getEndDate());
        return buildOrderCreateResultVO(order, totalAmount, totalDays);
    }


    /**
     * 查询订单详情
     * @param orderId 订单编号（orderNo）
     * @return 订单详情视图对象
     */
    @Override
    public OrderDetailVO getOrderDetail(String orderId) {
        // 1. 获取并校验当前用户（租户）
        Long currentUserId = getCurrentUserId();

        // 2. 查询订单存在性和所属权
        RentalOrder order = checkOrderIsExists(orderId, currentUserId);

        // 3. 查询订单关联的商品明细项
        List<RentalOrderItem> orderItems = rentalOrderItemMapper.selectByOrderId(order.getId());

        // 4. 查询商家信息
        User merchant = userMapper.selectById(order.getMerchantId());

        // 5. 构建订单详情 VO
        return buildOrderDetailVO(order, orderItems, merchant);
    }

    /**
     * 查询我的订单列表
     */
    @Override
    public PageResult<OrderDetailVO> getMyOrderList(TenantOrderQueryRequest queryRequest) {
        // 1. 获取当前登录用户的 ID
        Long currentUserId = getCurrentUserId();

        // 2. 订单状态校验（必须在 [1,6] 范围内）
        validateStatusIfPresent(queryRequest.getStatus());

        // 3. 分页查询当前用户的订单列表（支持按状态筛选）
        IPage<RentalOrder> orderPage = queryOrdersWithPagination(currentUserId, queryRequest);

        // 4. 若无订单数据，直接返回空分页结果
        if (orderPage.getRecords().isEmpty()) {
            return new PageResult<>(
                    queryRequest.getPage(),     // pageNum
                    queryRequest.getSize(),     // pageSize
                    0L,                         // total
                    Collections.emptyList()     // list
            );
        }

        // 5. 批量加载关联数据（订单项、商家信息）并构建 VO 列表
        List<OrderDetailVO> voList = buildOrderDetailVOList(orderPage.getRecords());

        // 6. 封装并返回分页结果
        return new PageResult<>(
                queryRequest.getPage(),     // 当前页码
                queryRequest.getSize(),     // 每页大小
                orderPage.getTotal(),       // 总记录数
                voList                      // 订单详情列表
        );
    }


    /**
     * 取消订单
     * @param orderNo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderDetailVO cancelOrder(String orderNo) {
        // 1. 获取当前用户登录ID
        Long currentUserId = getCurrentUserId();

        // 2. 校验订单存在性和所属权
        RentalOrder order = queryOrderByOrderNo(orderNo, currentUserId);

        // 3. 校验订单状态
        validateOrderCancelable(order);

        // 4. 修改订单状态
        updateOrderStatus(order);

        // 5. 查询订单关联的商品明细项
        List<RentalOrderItem> orderItems = rentalOrderItemMapper.selectByOrderId(order.getId());

        // 6. 查询商家信息
        User merchant = userMapper.selectById(order.getMerchantId());

        // 7. 构建订单详情 VO
        return buildOrderDetailVO(order, orderItems, merchant);
    }

    /**
     * 修改订单状态
     * @param order
     */
    private void updateOrderStatus(RentalOrder order) {
        order.setStatus(6);
        order.setUpdatedAt(LocalDateTime.now());
        rentalOrderMapper.updateById(order);
    }

    /**
     * 校验订单是否可取消
     * @param order
     * @return
     */
    private void validateOrderCancelable(RentalOrder order) {
        LocalDateTime cancelDeadline = order.getCreatedAt().plusMinutes(30);
        if (LocalDateTime.now().isAfter(cancelDeadline)) {
            throw OrderException.of(OrderErrorCode.CANCEL_WINDOW_EXPIRED);
        }

        if (order.getStatus() == 1) {
            return; // 允许取消
        }
        if (order.getStatus() == 6) {
            throw OrderException.of(OrderErrorCode.ORDER_ALREADY_CANCELLED);
        }
        // 其他所有非“待支付”且非“已取消”的状态，统一视为不可取消
        throw OrderException.of(OrderErrorCode.ORDER_CANNOT_BE_CANCELLED);
    }

    /**
     * 根据订单编号查询订单
     *
     * @param orderNo
     * @param userId
     * @return
     */
    private RentalOrder queryOrderByOrderNo(String orderNo, Long userId) {
        RentalOrder order = rentalOrderMapper.selectByOrderNo(orderNo);
        if (order == null) {
            throw OrderException.of(OrderErrorCode.ORDER_NOT_FOUND);
        }

        if (!order.getUserId().equals(userId)) {
            throw OrderException.of(OrderErrorCode.ORDER_ACCESS_DENIED);
        }

        return order;
    }

    /**
     * 如果状态非空，则校验其有效性 [1,6]
     */
    private void validateStatusIfPresent(Integer status) {
        if (status != null && (status < 1 || status > 6)) {
            throw OrderException.of(OrderErrorCode.INVALID_ORDER_STATUS_FILTER);
        }
    }

    /**
     * 分页查询当前用户的订单（支持按状态筛选）
     */
    private IPage<RentalOrder> queryOrdersWithPagination(Long userId, TenantOrderQueryRequest request) {
        return this.lambdaQuery()
                .eq(RentalOrder::getUserId, userId)
                .eq(request.getStatus() != null, RentalOrder::getStatus, request.getStatus())
                .orderByDesc(RentalOrder::getCreatedAt)
                .page(new Page<>(request.getPage(), request.getSize()));
    }

    /**
     * 批量构建订单详情 VO 列表（含商品项与商家信息）
     */
    private List<OrderDetailVO> buildOrderDetailVOList(List<RentalOrder> orders) {
        if (orders.isEmpty()) {
            return Collections.emptyList();
        }

        // 获取订单ID
        Set<Long> orderIds = orders.stream()
                .map(RentalOrder::getId)
                .collect(Collectors.toSet());

        // 获取商家ID
        Set<Long> merchantIds = orders.stream()
                .map(RentalOrder::getMerchantId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        // 根据订单ID批量查询订单项
        List<RentalOrderItem> allItems = rentalOrderItemMapper.selectByOrderIds(orderIds);
        Map<Long, List<RentalOrderItem>> itemsMap = allItems.stream()
                .collect(Collectors.groupingBy(RentalOrderItem::getOrderId));

        // 根据商家ID批量查询商家
        final Map<Long, User> merchantMap;
        if (merchantIds.isEmpty()) {
            merchantMap = Collections.emptyMap();
        } else {
            List<User> merchants = userMapper.selectBatchIds(new ArrayList<>(merchantIds));
            merchantMap = merchants.stream()
                    .collect(Collectors.toUnmodifiableMap(
                            User::getId,    // key：User 的 id
                            m -> m,         // value：User 对象本身
                            (u1, u2) -> u1  // 合并策略：如果 key 冲突（理论上不会），保留第一个
                    ));
        }

        // 构建 VO
        return orders.stream().map(order -> {
            List<RentalOrderItem> items = itemsMap.getOrDefault(order.getId(), Collections.emptyList());
            User merchant = merchantMap.get(order.getMerchantId());
            return buildOrderDetailVO(order, items, merchant);
        }).toList();
    }



    /**
     * 构建订单详情 VO 对象
     *
     * @param order   主订单实体
     * @param items   订单商品明细列表
     * @param merchant 商家用户信息（可能为 null）
     * @return 订单详情视图对象
     */
    private OrderDetailVO buildOrderDetailVO(RentalOrder order, List<RentalOrderItem> items, User merchant) {
        List<OrderItemVO> itemVOs = buildOrderItemVOs(items);
        String merchantName = getMerchantName(merchant);

        return OrderDetailVO.builder()
                .orderId(order.getId())
                .orderNo(order.getOrderNo())
                .userId(order.getUserId())
                .merchantId(order.getMerchantId())
                .merchantName(merchantName)
                .startDate(order.getStartDate())
                .endDate(order.getEndDate())
                .totalDays(order.getTotalDays())
                .totalAmount(order.getTotalAmount())
                .depositAmount(order.getDepositAmount())
                .paidAmount(order.getPaidAmount())
                .refundAmount(order.getRefundAmount())
                .paymentStatus(order.getPaymentStatus())
                .status(order.getStatus())
                .statusDesc(getStatusDesc(order.getStatus()))
                .remark(order.getRemark())
                .createdAt(order.getCreatedAt())
                .updatedAt(order.getUpdatedAt())
                .shipTime(order.getShipTime())
                .returnedAt(order.getReturnedAt())
                .payDeadline(getPayDeadline(order))
                .nextStepGuide(getNextStepGuide(order.getStatus()))
                .items(itemVOs)
                .build();
    }

    /**
     * 获取商家名称，若商家不存在则返回默认提示文本。
     *
     * @param merchant 商家用户实体，可能为 null
     * @return 商家店铺名称，或 "商家已注销"
     */
    private String getMerchantName(User merchant) {
        return merchant != null ? merchant.getShopName() : "商家已注销";
    }

    /**
     * 根据订单状态码转换为对应的中文描述。
     *
     * @param status 订单状态码（1-待支付, 2-已支付, 3-已取货, 4-已归还, 5-已取消）
     * @return 对应的中文状态描述；若状态码未知，返回 "未知状态"
     */
    private String getStatusDesc(Integer status) {
        return switch (status) {
            case 1 -> "待支付";
            case 2 -> "已支付";
            case 3 -> "已取货";
            case 4 -> "已归还";
            case 5 -> "已取消";
            default -> "未知状态";
        };
    }

    /**
     * 根据订单状态码生成面向用户地下一步操作引导文案。
     *
     * @param status 订单状态码
     * @return 用户友好的操作提示语；未知状态返回空字符串
     */
    private String getNextStepGuide(Integer status) {
        return switch (status) {
            case 1 -> "请在30分钟内完成支付，否则订单将自动取消";
            case 2 -> "商家正在准备发货，请耐心等待";
            case 3 -> "商品已发出，请注意查收并按时取用";
            case 4 -> "商品已归还，等待商家确认";
            case 5 -> "订单已取消";
            default -> "";
        };
    }

    /**
     * 计算订单的支付截止时间（仅适用于“待支付”状态）。
     * 支付截止时间为订单创建时间后 30 分钟。
     *
     * @param order 租赁订单实体，不可为 null
     * @return 支付截止时间；若订单非待支付状态或创建时间为空，则返回 null
     */
    private LocalDateTime getPayDeadline(RentalOrder order) {
        if (order.getStatus() == 1 && order.getCreatedAt() != null) {
            return order.getCreatedAt().plusMinutes(30);
        }
        return null;
    }

    /**
     * 将订单商品明细列表转换为前端展示用的商品项 VO 列表。
     * 若商品已被下架（查询不到），则显示默认名称。
     *
     * @param items 订单商品明细列表，不可为 null
     * @return 转换后的 {@link OrderItemVO} 列表
     */
    private List<OrderItemVO> buildOrderItemVOs(List<RentalOrderItem> items) {
        // 1. 空列表保护：避免无效查询和空指针
        if (items.isEmpty()) {
            return Collections.emptyList();
        }

        // 2. 提取所有不重复的服装 ID，用于批量查询商品信息
        Set<Long> costumeIds = items.stream()
                .map(RentalOrderItem::getCostumeId)
                .collect(Collectors.toSet());

        // 3. 批量查询商品数据，并构建 ID -> 商品对象的映射，便于 O(1) 快速查找
        List<Costume> costumes = costumeMapper.selectBatchIds(costumeIds);
        Map<Long, Costume> costumeMap = costumes.stream()
                .collect(Collectors.toMap(Costume::getId, c -> c));

        // 4. 将每个订单商品项映射为前端 VO，补充商品名称、图片等展示信息
        return items.stream().map(item -> {
            Costume costume = costumeMap.get(item.getCostumeId());
            return OrderItemVO.builder()
                    .costumeId(item.getCostumeId())
                    .costumeName(costume != null ? costume.getName() : "商品已下架")
                    .costumeImage(costume != null ? costume.getImageUrl() : null)
                    .quantity(item.getQuantity())
                    .dailyPrice(item.getDailyPrice()) // 使用下单时快照价格，非实时价格
                    .build();
        }).toList();
    }

    /**
     * 检查订单是否存在及所属权
     *
     * @param orderId 订单编号（orderNo）
     * @param currentUserId 当前用户ID
     * @return 订单实体
     */
    private RentalOrder checkOrderIsExists(String orderId, Long currentUserId) {
        RentalOrder order = rentalOrderMapper.selectByOrderNo(orderId);
        if (order == null) {
            throw OrderException.of(OrderErrorCode.ORDER_NOT_FOUND);
        }

        if (!order.getUserId().equals(currentUserId)) {
            throw OrderException.of(OrderErrorCode.ORDER_NOT_OWNED_BY_USER);
        }

        return order;
    }

    /**
     * 构建并保存租赁订单主记录到数据库
     *
     * @param createRequest   订单创建请求参数
     * @param currentUserId   当前租户用户ID
     * @param totalAmount     订单总金额
     * @return 已保存的租赁订单实体
     */
    private RentalOrder buildAndSaveOrder(OrderCreateRequest createRequest, Long currentUserId, BigDecimal totalAmount) {
        String orderNo = "R" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));

        RentalOrder order = new RentalOrder();
        order.setOrderNo(orderNo);
        order.setUserId(currentUserId);
        order.setStartDate(createRequest.getStartDate());
        order.setEndDate(createRequest.getEndDate());
        long totalDays = ChronoUnit.DAYS.between(createRequest.getStartDate(), createRequest.getEndDate());
        order.setTotalDays((int) totalDays);
        order.setTotalAmount(totalAmount);
        order.setStatus(1); // 1 = 待支付
        order.setRemark(createRequest.getRemark());

        rentalOrderMapper.insert(order);
        return order;
    }

    /**
     * 构建订单创建结果的 VO 对象
     *
     * @param order       已创建的订单实体
     * @param totalAmount 订单总金额
     * @param totalDays   租赁总天数
     * @return 订单创建结果视图对象
     */
    private OrderCreateResultVO buildOrderCreateResultVO(RentalOrder order, BigDecimal totalAmount, long totalDays) {
        return OrderCreateResultVO.builder()
                .orderNo(order.getOrderNo())
                .totalAmount(totalAmount)
                .totalDays((int) totalDays)
                .createdAt(order.getCreatedAt() != null ? order.getCreatedAt() : LocalDateTime.now())
                .payDeadline(LocalDateTime.now().plusMinutes(30))
                .build();
    }

    /**
     * 校验租赁起止日期是否符合业务规则
     *
     * @param request 订单创建请求
     */
    private void validateDateRange(OrderCreateRequest request) {
        LocalDate startDate = request.getStartDate();
        LocalDate endDate = request.getEndDate();
        LocalDate today = LocalDate.now();

        if (startDate == null) {
            throw OrderException.of(OrderErrorCode.START_DATE_REQUIRED);
        }
        if (endDate == null) {
            throw OrderException.of(OrderErrorCode.END_DATE_REQUIRED);
        }
        if (startDate.isBefore(today)) {
            throw OrderException.of(OrderErrorCode.START_DATE_BEFORE_TODAY);
        }
        if (!endDate.isAfter(startDate)) {
            throw OrderException.of(OrderErrorCode.END_DATE_NOT_AFTER_START);
        }

        long days = ChronoUnit.DAYS.between(startDate, endDate);
        if (days < 1) {
            throw OrderException.of(OrderErrorCode.RENTAL_DURATION_TOO_SHORT);
        }
        if (days > 30) {
            throw OrderException.of(OrderErrorCode.RENTAL_DURATION_TOO_LONG);
        }
    }

    /**
     * 批量校验服装商品是否存在、已上架，并验证其所属商家状态
     *
     * @param request 订单创建请求
     * @return 有效的服装ID到实体的映射
     */
    private Map<Long, Costume> validateAndFetchCostumes(OrderCreateRequest request) {
        Set<Long> costumeIds = new HashSet<>();
        for (OrderCreateRequest.OrderItemRequest item : request.getItems()) {
            Long cid = item.getCostumeId();
            if (cid == null || cid <= 0) {
                throw OrderException.of(OrderErrorCode.COSTUME_ID_INVALID);
            }
            costumeIds.add(cid);
        }

        // 批量查询服装信息
        List<Costume> costumes = costumeMapper.selectBatchIds(costumeIds);
        Map<Long, Costume> costumeMap = new HashMap<>();
        for (Costume c : costumes) {
            costumeMap.put(c.getId(), c);
        }

        // 逐项校验商品及商家状态
        for (OrderCreateRequest.OrderItemRequest item : request.getItems()) {
            Long cid = item.getCostumeId();
            Costume costume = costumeMap.get(cid);

            if (costume == null) {
                throw OrderException.of(OrderErrorCode.COSTUME_NOT_AVAILABLE, "服装ID不存在: " + cid);
            }

            // 状态 3 表示“已上架”
            Byte status = costume.getStatus();
            if (status == null || status != 3) {
                throw OrderException.of(OrderErrorCode.COSTUME_NOT_AVAILABLE, "服装未上架或不可租: " + cid);
            }

            validateMerchantStatus(costume.getMerchantId());
        }

        return costumeMap;
    }

    /**
     * 验证商家账户是否有效且角色正确
     *
     * @param merchantId 商家用户ID
     */
    private void validateMerchantStatus(Long merchantId) {
        if (merchantId == null || merchantId <= 0) {
            throw OrderException.of(OrderErrorCode.MERCHANT_ID_INVALID);
        }
        User merchant = userMapper.selectById(merchantId);
        if (merchant == null ||
                merchant.getAccountStatus() != AccountStatus.ENABLED ||
                merchant.getRole() != UserRole.MERCHANT) {
            throw OrderException.of(OrderErrorCode.COSTUME_NOT_AVAILABLE, "关联商家不可用");
        }
    }

    /**
     * 计算订单总金额：日租金 × 租期 × 数量
     *
     * @param request      订单创建请求
     * @param costumeMap   有效的服装映射
     * @return 订单总金额（保留两位小数）
     */
    private BigDecimal calculateTotalAmount(OrderCreateRequest request, Map<Long, Costume> costumeMap) {
        LocalDate startDate = request.getStartDate();
        LocalDate endDate = request.getEndDate();
        long totalDays = ChronoUnit.DAYS.between(startDate, endDate);
        BigDecimal total = BigDecimal.ZERO;

        for (OrderCreateRequest.OrderItemRequest item : request.getItems()) {
            Integer quantity = item.getQuantity();
            if (quantity == null || quantity < 1) {
                throw OrderException.of(OrderErrorCode.QUANTITY_INVALID);
            }

            Costume costume = costumeMap.get(item.getCostumeId());
            BigDecimal dailyPrice = costume.getRentalPrice();
            if (dailyPrice == null || dailyPrice.compareTo(BigDecimal.ZERO) <= 0) {
                throw OrderException.of(OrderErrorCode.COSTUME_PRICE_INVALID, "服装日租金无效: " + costume.getId());
            }

            BigDecimal itemTotal = dailyPrice.multiply(BigDecimal.valueOf(totalDays))
                    .multiply(BigDecimal.valueOf(quantity));
            total = total.add(itemTotal);
        }

        if (total.compareTo(BigDecimal.ZERO) <= 0) {
            throw OrderException.of(OrderErrorCode.CREATE_ORDER_FAILED, "计算出的订单金额无效");
        }

        return total.setScale(2, java.math.RoundingMode.HALF_UP);
    }

    /**
     * 从安全上下文中获取当前登录用户 ID
     *
     * @return 当前用户ID
     */
    private Long getCurrentUserId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            throw AuthException.of(AuthErrorCode.TOKEN_MISSING);
        }

        Object principal = authentication.getPrincipal();
        if (!(principal instanceof LoginUser loginUser)) {
            throw AuthException.of(AuthErrorCode.TOKEN_INVALID);
        }

        Long userId = loginUser.getUserId();
        if (userId == null || userId <= 0) {
            throw AuthException.of(AuthErrorCode.USER_ID_INVALID);
        }

        validateUserIdentity(userId);
        return userId;
    }

    /**
     * 校验用户身份：存在、启用、角色为普通用户（租户）
     *
     * @param userId 用户ID
     */
    private void validateUserIdentity(Long userId) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw AuthException.of(AuthErrorCode.USER_NOT_EXIST);
        }
        if (user.getAccountStatus() != AccountStatus.ENABLED) {
            throw AuthException.of(AuthErrorCode.ACCOUNT_DISABLED);
        }
        if (user.getRole() != UserRole.CUSTOMER) {
            throw AuthException.of(AuthErrorCode.REQUIRED_USER_ROLE, "仅普通用户可创建租赁订单");
        }
    }
}